package state;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Controller("threadState")
@Slf4j
public class StateController {

    Object lock = new Object();

    private ThreadPoolExecutor pool = new ThreadPoolExecutor(
            5,5, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
    );


    /**
     * 当前线程                          其他线程
     * ----------------------------------------------------------
     * synchronized(lock){               synchronized(lock){
     *     lock.wait(100s);  ───────┐         lock.notifyAll();
     * }                            │
     *                              ▼
     *                 进入锁竞争池，状态=BLOCKED
     *                              │
     *                 抢到锁后状态=RUNNABLE
     *                              │
     *                 检查中断标志
     *                 /         \
     *            未中断         已中断
     *             |               \
     *         从wait()返回     抛InterruptedException
     *             |               \
     *         继续往下执行        进入catch块
     *
     *         notifyAll() 只负责“叫醒”，线程必须重新拿到锁、并经过中断检查，
     *         才能真正从 wait() 返回并继续往下走；在此之前它处于 BLOCKED（抢锁）或 RUNNABLE（抢到了但还没完成检查）。
     *
     * @param waitTimeSec
     */
    @RequestMapping("addWaitTask")
    @ResponseBody
    public void addWaitTask(@RequestParam("waitTimeSec") int waitTimeSec) {
        log.info("invoke addWaitTask");
        pool.submit(
                () -> {
                    synchronized (lock) {
                        log.info("luck:got the lock!");
                        long deadline = waitTimeSec * 1000 + System.currentTimeMillis();
                        try {
                            log.info("addWaitTask, to wait {} secs", waitTimeSec);
                            lock.wait(waitTimeSec * 1000);
                            // 超时等待后，加入block队列竞争monitor
                            log.info(">>>>hi, i am back!");
                            long remainTime = deadline - System.currentTimeMillis();
                            log.info(">>>lock interrupted, remain:{} ms, thread state:{}",
                                    remainTime, Thread.currentThread().getState());
                        } catch (InterruptedException e) {
                            log.info("interrupted:{}", e);
                        }
                    }
                    log.info(">>> at the end of line, bye!!!");
                }
        );
    }

    @RequestMapping("addSleepTask")
    @ResponseBody
    public void addSleepTask(@RequestParam("sleepTimeSec") int sleepTimeSec) {
        log.info("invoke addSleepTask");
        pool.submit(
                () -> {
                    synchronized (lock) {
                        log.info("so lucky:got the lock!");
                        try {
                            Thread.sleep(sleepTimeSec * 1000);
                            log.info("after sleep, go to exit...");
                        } catch (InterruptedException e) {
                            log.info("interrupted:{}", e);
                        }
                    }
                    log.info(">>> at the end of sleep, bye!!!");
                }
        );
    }

    @RequestMapping("interrupt")
    @ResponseBody
    public String interrupt() {
        synchronized (lock) {
            log.info("to interrupt some waiting thread!!!");
            lock.notifyAll();
        }
        return "ok";
    }

    @RequestMapping("info")
    @ResponseBody
    public String info() {
        return ThreadStateDemo.poolInfo();
    }

}
